package top.saodisheng.proxy.demo;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import top.saodisheng.proxy.DefaultEchoService;
import top.saodisheng.proxy.EchoService;

import java.lang.reflect.Method;

/**
 * JDK 动态代理的目标对象必须是一个接口，在我们日常生活中，无法避免开发人员不写接口直接写类，或者根本不需要接口，直接用类进行表达。
 * 这个时候我们就需要通过一些字节码提升的手段，来帮助做这个事情，在运行时，非编译时，来创建新的 Class 对象，这种方式称之为字节码提升。
 * 在 Spring 内部有两个字节码提升的框架，ASM（过于底层，直接操作字节码）和 CGLIB（相对于前者更加简便）。
 * CGLIB 动态代理则是基于类代理（字节码提升），通过 ASM（Java 字节码的操作和分析框架）将被代理类的 class 文件加载进来，通过修改其字节码生成子类来处理。
 *
 * CGLIB动态代理在JVM运行时会新生成一个代理类，这个代理对象继承了目标类，也就是创建了一个目标子类，新生成的代理对象的Class对象会继承superClass被代理的类。
 * 在重写的方法中会调用callback回调方法（方法拦截器）进行处理。从而字节码得到提升。
 *
 * 通过下面的例子可以看到Callback是直接创建的，在intercept(...)方法中拦截DefaultEchoService的方法。因为MethodInterceptor继承了Callback回调接口，
 * 所以这里传入一个MethodInterceptor方法拦截器是没有问题的。
 * 注意：假如你想设置一个Callback[]数组去处理不同的方法，那么需要设置一个CallbackFilter筛选器，用于选择这个方法使用数组中的哪个Callback去处理。
 * @author saodisheng
 * @date 2021/9/13
 */
public class CglibDynamicProxyDemo {
	public static void main(String[] args) {
		// 创建CGLIB增强对象
		Enhancer enhancer = new Enhancer();
		// 指定父类，也就是被代理的类
	 	Class<?> superClass = DefaultEchoService.class;
	 	// 设置被代理的类
	 	enhancer.setSuperclass(superClass);
	 	// 指定回调接口（拦截器）
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object source, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
				long startTime = System.currentTimeMillis();
				Object result = methodProxy.invokeSuper(source, args);
				long costTime = System.currentTimeMillis() - startTime;
				System.out.println("[CGLIB 字节码提升] echo 方法执行的实现：" + costTime + " ms.");
				return result;
			}
		});

		// 创建代理对象
		EchoService echoService = (EchoService) enhancer.create();
		// 输出执行结果
		System.out.println(echoService.echo("hello, world, this is saodisheng"));
	}
}